;=================================================
; collections sample
;
(load "common.lsp")

;=================================================
; :DOC: vectors syn:
; (vector)     ==> #()
; (vector 1)   ==> #(1)
; (vector 1 2) ==> #(1 2)
;
; making array w/5 elements
; (make-array 5 :initial-element nil) ==> #(NIL NIL NIL NIL NIL)
;
; making 5-elements empty array
; (make-array 5 :fill-pointer 0) ==> #()
;
; making arbitrarily resizeable vector
; (make-array 5 :fill-pointer 0 :adjustable t) ==> #()
; To add elements to an adjustable vector use VECTOR-PUSH-EXTEND
;
; making a string
; (make-array 5 :fill-pointer 0 :adjustable t :element-type 'character)  ==> ""
;
; The type descriptor to pass as the :element-type to create a bit vector is the symbol BIT
; bitvectors has its own print syntax that looks like #*011011011
(defun test-vec ()
 ;
 )

;=================================================
; operate w/ hash table
;
(defparameter *h* (make-hash-table))
(defun test-hash-table ()

 ; simple sample
 (trace-fn "hash sample:" (gethash 'foo *h*))
 (setf (gethash 'foo *h*) 'quux)
 (trace-fn (gethash 'foo *h*))

 ;
 (defun show-value (key hash-table)
  (multiple-value-bind (value present) (gethash key hash-table)
    (if present
      (format nil "Value ~a actually present." value)
      (format nil "Value ~a because key not found." value))))

 (setf (gethash 'bar *h*) nil) ; provide an explicit value of NIL

 (trace-fn
  "show-value sample"
  (show-value 'foo *h*)
  (show-value 'bar *h*)
  (show-value 'baz *h*)
  )

 ; iteration
 (maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) *h*)
 ; using remhash to remove certain entries
 ;(maphash #'(lambda (k v) (when (< v 10) (remhash k *h*))) *h*)

 (format t "=====~%")

 ; iteration using loop
 (loop for k being the hash-keys in *h* using (hash-value v)
  do (format t "~a => ~a~%" k v))


 )

;=================================================
; collections test
;
(defun test ()
 ; 1
 (trace-fn
  "vectors"
  (vector 1 2 'a 'b 'c)
  (make-array 5 :initial-element nil)
  (make-array 5 :fill-pointer 0)

  ; make a string
  (make-array 5 :fill-pointer 0 :adjustable t :element-type 'character)
  (make-array 3 :initial-element #\w :element-type 'character)

  ; make a bit vector
  (make-array 4 :initial-element 1 :element-type 'bit)
  )

 ; 2
 (let ((xvec (vector 1 2 3)))

  (setf (elt xvec 2) 300)

  (format t "vec = ~a, l = ~a, e0 = ~a, e2 = ~a~%"
   xvec
   (length xvec)
   (elt xvec 0)
   (elt xvec 2)
   ;(elt xvec 3) produces an error
   )
  ) ; let

 ; 3
 ; Name	Required Arguments	Returns
 ; COUNT	Item and sequence	Number of times item appears in sequence
 ; FIND	Item and sequence	Item or NIL
 ; POSITION	Item and sequence	Index into sequence or NIL
 ; REMOVE	Item and sequence	Sequence with instances of item removed
 ; SUBSTITUTE	New item, item, and sequence	Sequence with instances of item replaced with new item
 ;
 ; std arguments
 ; Argument	Meaning	Default
 ; :test	Two-argument function used to compare item (or value extracted by :key function) to element.	EQL
 ; :key	One-argument function to extract key value from actual sequence element. NIL means use element as is.	NIL
 ; :start	Starting index (inclusive) of subsequence.	0
 ; :end	Ending index (exclusive) of subsequence. NIL indicates end of sequence.	NIL
 ; :from-end	If true, the sequence will be traversed in reverse order, from end to start.	NIL
 ; :count	Number indicating the number of elements to remove or substitute or NIL to indicate all (REMOVE and SUBSTITUTE only).	NIL

 (trace-fn
  "Sequence Iterating Functions"
  (count 1 #(1 2 1 2 3 1 2 3 4))
  (remove 1 #(1 2 1 2 3 1 2 3 4))
  (remove 1 '(1 2 1 2 3 1 2 3 4))
  (remove #\a "foobarbaz")
  (substitute 10 1 #(1 2 1 2 3 1 2 3 4))
  (substitute 10 1 '(1 2 1 2 3 1 2 3 4))
  (substitute #\x #\b "foobarbaz")
  (find 1 #(1 2 1 2 3 1 2 3 4)) 
  (find 10 #(1 2 1 2 3 1 2 3 4))
  (position 1 #(1 2 1 2 3 1 2 3 4))
  
  "a bit more complicated samples"
  (count "foo" #("foo" "bar" "baz") :test #'string=)
  (find 'c #((a 10) (b 20) (c 30) (d 40)) :key #'first)
  (find 'a #((a 10) (b 20) (a 30) (b 40)) :key #'first)
  (find 'a #((a 10) (b 20) (a 30) (b 40)) :key #'first :from-end t)
  
  "higher-order function variants"
  (count-if #'evenp #(1 2 3 4 ))
  (count-if-not #'evenp #(1 2 3 4 5))
  (position-if #'digit-char-p "abcd0001")
  (remove-if-not #'(lambda (x) (char= (elt x 0) #\f))
   #("foo" "bar" "baz" "foom"))
  (count-if #'evenp #((1 a) (2 b) (3 c) (4 d) (5 e)) :key #'first)
  (count-if-not #'evenp #((1 a) (2 b) (3 c) (4 d) (5 e)) :key #'first)
  (remove-if-not #'alpha-char-p
   #("foo" "bar" "1baz") :key #'(lambda (x) (elt x 0)))
  (remove-duplicates #(1 2 1 2 3 1 2 3 4))

  "whole sequence manipulations"
  (concatenate 'vector #(1 2 3) '(4 5 6))
  (concatenate 'list #(1 2 3) '(4 5 6)) 
  (concatenate 'string "abc" '(#\d #\e #\f))

  "sort and merging"
  (sort (vector "foo" "bar" "baz") #'string<)
  (merge 'vector #(1 3 5) #(2 4 6) #'<)
  (merge 'list #(1 3 5) #(2 4 6) #'<)

  "subsequence manipulations"
  (subseq "foobarbaz" 3)
  (subseq "foobarbaz" 3 6)
  (position #\b "foobarbaz")
  (search "bar" "foobarbaz")
  (mismatch "foobarbaz" "foom")
  (mismatch "foobar" "bar" :from-end t)

  "sequence predicates"
  ; Here are some examples of testing just one sequence:
  (every #'evenp #(1 2 3 4 5))
  (some #'evenp #(1 2 3 4 5))
  (notany #'evenp #(1 2 3 4 5))
  (notevery #'evenp #(1 2 3 4 5))
  ; These calls compare elements of two sequences pairwise:
  (every #'> #(1 2 3 4) #(5 4 3 2))
  (some #'> #(1 2 3 4) #(5 4 3 2))
  (notany #'> #(1 2 3 4) #(5 4 3 2))
  (notevery #'> #(1 2 3 4) #(5 4 3 2))

  "mapping"
  (map 'vector #'* #(1 2 3 4 5) #(10 9 8 7 6))
  ; (map-into a #'+ a b c) - does not produce new sequence, but places the result into the first one argument
  (reduce #'+ #(1 2 3 4 5 6 7 8 9 10))
  ) ; trace-fn

  ; 3
  (test-hash-table)
 )

;=================================================
; app entry point
;
(defun main ()

 (format t "collections sample~%=====~%")

 (test)

 (exit)
 )

;=================================================
; executes main
;
(main)
